Skip to main content

第 10 章:設計網路環境 Network

Kubernetes 網路模型

  • 所有 Nodes 上的所有 Pods 都可以相互通訊
  • Nodes 上的 agent 可以跟所有 Pods 溝通
  • No Network Address Translation (NAT)

Kubernetes 網路拓墣

  • Node Network
  • Pod Network
  • Cluster Network (used by Services)

Pod 網路

  • 安裝 network 套件
sudo apt install berdge-utils net-tools
kubectl get pods -o wide -A
NAMESPACE      NAME                                READY   STATUS        RESTARTS   AGE    IP            NODE        NOMINATED NODE   READINESS GATES
default apiserver 2/2 Terminating 0 20m 172.16.2.11 k8s-wrk-2 <none> <none>
kube-flannel kube-flannel-ds-75hpx 1/1 Running 0 7h5m 10.0.1.70 k8s-msr-1 <none> <none>
kube-flannel kube-flannel-ds-gpgwh 1/1 Running 0 7h4m 10.0.1.205 k8s-wrk-1 <none> <none>
kube-flannel kube-flannel-ds-zcfzv 1/1 Running 0 7h3m 10.0.1.118 k8s-wrk-2 <none> <none>
kube-system coredns-5dd5756b68-czq7d 1/1 Running 0 7h5m 172.16.0.3 k8s-msr-1 <none> <none>
kube-system coredns-5dd5756b68-qxvzw 1/1 Running 0 7h5m 172.16.0.2 k8s-msr-1 <none> <none>
kube-system etcd-k8s-msr-1 1/1 Running 0 7h5m 10.0.1.70 k8s-msr-1 <none> <none>
kube-system kube-apiserver-k8s-msr-1 1/1 Running 0 7h5m 10.0.1.70 k8s-msr-1 <none> <none>
kube-system kube-controller-manager-k8s-msr-1 1/1 Running 0 7h5m 10.0.1.70 k8s-msr-1 <none> <none>
kube-system kube-proxy-5rtkg 1/1 Running 0 7h4m 10.0.1.205 k8s-wrk-1 <none> <none>
kube-system kube-proxy-pshqj 1/1 Running 0 7h3m 10.0.1.118 k8s-wrk-2 <none> <none>
kube-system kube-proxy-w75jn 1/1 Running 0 7h5m 10.0.1.70 k8s-msr-1 <none> <none>
kube-system kube-scheduler-k8s-msr-1 1/1 Running 0 7h5m 10.0.1.70 k8s-msr-1 <none> <none>

sudo tcpdump -i enp0s8 -w test.pcap &
ip -c a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 0a:1d:4b:00:a2:3d brd ff:ff:ff:ff:ff:ff
inet 10.0.1.233/24 metric 100 brd 10.0.1.255 scope global dynamic eth0
valid_lft 2360sec preferred_lft 2360sec
inet6 fe80::81d:4bff:fe00:a23d/64 scope link
valid_lft forever preferred_lft forever
3: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8951 qdisc noqueue state UNKNOWN group default
link/ether ca:1b:a3:ce:43:ab brd ff:ff:ff:ff:ff:ff
inet 172.16.0.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::c81b:a3ff:fece:43ab/64 scope link
valid_lft forever preferred_lft forever
4: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8951 qdisc noqueue state UP group default qlen 1000
link/ether 9a:e7:c4:4d:d0:ed brd ff:ff:ff:ff:ff:ff
inet 172.16.0.1/24 brd 172.16.0.255 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::98e7:c4ff:fe4d:d0ed/64 scope link
valid_lft forever preferred_lft forever
5: veth7635aa34@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8951 qdisc noqueue master cni0 state UP group default
link/ether 1e:7a:ff:77:b1:e4 brd ff:ff:ff:ff:ff:ff link-netns cni-25c75185-fe4c-98af-72ee-e991462b4715
inet6 fe80::1c7a:ffff:fe77:b1e4/64 scope link
valid_lft forever preferred_lft forever
6: veth3ee80888@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8951 qdisc noqueue master cni0 state UP group default
link/ether de:41:5c:d2:56:c9 brd ff:ff:ff:ff:ff:ff link-netns cni-7c4b56a8-7b8c-adb6-25f1-2c8349da932b
inet6 fe80::dc41:5cff:fed2:56c9/64 scope link
valid_lft forever preferred_lft forever
  • in the same node, pods can communicate each other because of CNI bridge
  • in the different nodes,
    • using flannel to package the data and send it to enp (network segment)

Cluster DNS:K8S 叢集內建 DNS 服務

  • Cluster DNS configuration
    • 在 master node 上,1 deployment (1 replicaset) with 2 pods
    • 1 kube-dns service

DNS is available as a Service in a Cluster, Pods are configured to use this DNS.

kubectl get pod -A
kubectl run client --image=xiaopeng163/net/box --command --sh -c "sleep 100000"
kubectl exec -it client -- cat /etc/resolv.conf // the upper pod's DNS doc

DNS is available as a Service in a Cluster, Pods are configured to use this DNS.

kubectl describe configmaps coredns --namespace kube-system

In general a Pod has the following DNS resolution:

pod-ip-address.my-namespace.pod.cluster-domain.example

Service:將 Pod 集合成一可連線端點

  • Service 概念
    • 將一組 Pod 聚集成單一資源,而使用者可以設定利用多種方式來存取這組 Pod 端點
    • 產生一個穩定的叢集內部 IP 位址
  • Service 連線方式
    • Request → service → cluster IP → Pod
  • Service 支援類型
    • Cluster IP
    • NodePort
    • Load Balancer
    • ExternalName
  • Service 帶來的好處
    • 提供客戶端的持久端點
    • 提供持久 virtual IP 和 DNS domain
    • 後端 Pod 的負載平衡
    • 在 Pod 控制器操作期間自動更新
  • Service 如何運作
    • Service 會根據 Labels 與 Selectors 找到對應的 pods
    • 建立並註冊 Pod 的 IP/port 在 service
    • 建立網路資訊在 Node 上的 kube-proxy 與 iptables
    • kube-proxy 分發需求到對應的 pods

Cluster IP:建立內部訪問用虛擬 IP

  • 用於內部訪問,設定 Cluster IP 建立 internal private IP

範例

  • 創建 deployment
kubectl apply -f helloword.yaml
helloword.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
spec:
replicas: 1
selector:
matchLabels:
app: hello-world
template:
metadata:
labels:
app: hello-world
spec:
containers:
- name: hello-world
image: gcr.io/google-samples/hello-app:1.0
ports:
- containerPort: 8080
  • 擴展 replicas
kubectl scale --replicas=2 deployment/hello-world
  • 創建 service
kubectl expose deployment hello-world --port =80  --target-port =8080 
  • 查看 service
kubectl get svc hello-world -o yaml

NodePort:可透過 Node 的 Port 直接存取 Pod

使得可以在 cluster 中每個 Node 上的 port 上存取該服務

kubectl expose deployment hello-world  --target-port =8080  --type = NodePort
kubectl get svc
sudo iptables -t nat -L PREROUTING  |  column -t
sudo iptables -t nat -L KUBE-SERVICES  -n   |  column -t
sudo iptables -t nat -L KUBE-NODEPORTS  -n   |  column -t
sudo iptables -t nat -L KUBE-EXT-DZ6LTOHRG6HQWHYE  -n   |  column -t

LoadBalancer 外接雲端服務的負載均衡器

可以透過雲端服務的負載平衡器功能從外部存取。 GCP、AWS、Azure 和 OpenStack 提供此功能

kubectl expose deployment hello-world --port=80 --target-port=8080 --type=LoadBalancer

Service Discovery 服務發現

為什麼需要服務發現

  • 同時建構服務,需要讓一個服務存取另一個服務
  • 但是當你建立一個服務時,你需要先知道群集IP,因此你需要使用服務發現

DNS 建立服務時自動生成

kubectl create deployment demo --image=[gcr.io/google-samples/hello-app:1.0](http://gcr.io/google-samples/hello-app:1.0) --port=8080
kubectl expose deployment demo
kubectl get svc --namespace kube-system
nslookup demo.default.svc.cluster.local 10.96.0.10

會自動產生以下的 DNS domain

  • <service name>.<namespace>.svc.cluster.local
  • 這時不僅有一個 cluster IP 可供整個 cluster 訪問,同時一個 DNS 網域也被註冊了

ENV 手動註冊環境變量

另外一些環境變數也會在叢集內註冊。 這時候如果我們創建另外一個 POD

kubectl run client --image=xiaopeng163/net-box --command -- sh -c "sleep 100000"
kubectl exec -it client -- sh
env | grep DEMODEMO_SERVICE_HOST=10.105.71.223
DEMO_PORT_8080_TCP_ADDR=10.105.71.223
DEMO_PORT_8080_TCP_PORT=8080DEMO_PORT_8080_TCP_PROTO=tcp
DEMO_PORT=tcp://10.105.71.223:8080
DEMO_SERVICE_PORT=8080DEMO_PORT_8080_TCP=tcp://10.105.71.223:8080

外部服務 service

創建一個無 label selector 的 service,像是一個外部的資料庫

kind: Endpoints
apiVersion: v1
metadata:
name: my-service
subsets:
- address:
- IP: 1.2.3.4
ports:
- port: 80
## 拓撲感知路由

拓撲感知路由指的是客戶端對一個服務的訪問流量,可以根據這個服務的端點拓撲,優先路由到與該客戶端在同一個節點或者可用區的端點上的路由行為。

先決條件
為了開啟服務感知路由,你需要:

- 開啟 TopologyAwareHints 智能感知提示門控
- 開啟 EndpointSlice 控制器
- 安裝 kube-proxy

### 端點切片 EndpointSlice
我們知道 Endpoint 通常情況下是由 Service 資源自動創建和管理的,但是隨著 Kubernetes 集群的規模越來越大和管理的服務越來越多,Endpoint API 的局限性變得越來越明顯。 端點切片(EndpointSlices)提供了一種簡單的方法來跟蹤 Kubernetes 集群中的網絡端點。它們為 Endpoint 提供了一種可伸縮和可拓展的替代方案,同時還可以被用到拓撲感知路由中。

```yaml
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: example-hints
labels:
kubernetes.io/service-name: example-svc
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 80
endpoints:
- addresses:
- "10.127.2.3"
conditions:
ready: true
hostname: pod-1
nodename: node-a
zone: zone-a

EndpointSlice 中的每個端點都可以包含一定的拓撲信息。拓撲信息包括端點的位置,對應節點、可用區的信息。這些信息體現為 EndpointSlices 的如下端點字段:

  • nodeName - 端點所在的 Node 名稱
  • zone - 端點所處的可用區
  • hostname - 端點的 pod 名稱

啟用拓撲感知

請啟用 kube-apiserver、kube-controller-manager、和 kube-proxy 的特性門控 TopologyAwareHints。通過把 Service 中的注解 service.kubernetes.io/topology-aware-hints 的值設置為 auto,來激活服務的拓撲感知提示功能。這告訴 EndpointSlice 控制器在它認為安全的時候來設置拓撲提示。kube-proxy 組件依據 EndpointSlice 控制器設置的提示,過濾由它負責路由的端點。

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: example-hints
labels:
kubernetes.io/service-name: example-svc
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 80
endpoints:
- addresses:
- "10.1.2.3"
conditions:
ready: true
hostname: pod-1
zone: zone-a
hints:
forZones:
- name: "zone-a"

拓撲感知路由管理

在大多數場合下,EndpointSlice 都由某個 Service 所有,因為端點切片正是為該服務跟蹤記錄其端點。這一屬主關系是通過為每個 EndpointSlice 設置一個 屬主(owner)引用,同時設置 kubernetes.io/service-name 標簽來標明的,目的是方便查找隸屬於某服務的所有 EndpointSlice。

控制面(尤其是端點切片的控制器)會創建和管理 EndpointSlice 對象。EndpointSlice 對象還有一些其他使用場景,例如作為服務網格(Service Mesh)的實現。這些場景都會導致有其他實體 或者控制器負責管理額外的 EndpointSlice 集合。

為了確保多個實體可以管理 EndpointSlice 而且不會相互產生幹擾,Kubernetes 定義了標簽 endpointslice.kubernetes.io/managed-by,用來標明哪個實體在管理某個 EndpointSlice。端點切片控制器會在自己所管理的所有 EndpointSlice 上將該標簽值設置 為 endpointslice-controller.k8s.io。管理 EndpointSlice 的其他實體也應該為此標簽設置一個唯一值。

Ingress

接收請求並根據一定的路由規則,把請求轉送到對應的 Service 上去

  • Ingress Resource,就是一系列的路由規則 routing rules
  • Ingress Controller, 控制實作這些路由規則。

建立 Nginx ingress Controller

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.2.1

Exposing a Single Service with Ingress

singleingress

  • 建立 web server deployment 與 service
    • $ kubectl create deployment demo --image = httpd --port =80
    • $ kubectl expose deployment demo
  • 建立 ingress resource
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-localhost
namespace: default
spec:
ingressClassName: nginx
rules:
- host: demo.localdev.me
http:
paths:
- backend:
service:
name: demo
port:
number: 80
path: /
pathType: Prefix
  • 直接访问 service 的 clusterIP 是可以的
  • 但是直接访问 ingress-nginx-controller 的 ClusterIP 是不可以的

Exposing Multiple Services with Ingress

mutiingress

建立兩個 deployment 與兩個 Service

kubectl create deployment web1 --image = gcr.io/google-samples/hello-app:1.0 --port =8080  --replicas =2 
kubectl expose deployment web1 --port 9001 --target-port 8080
kubectl create deployment web2 --image = gcr.io/google-samples/hello-app:2.0 --port =8080 --replicas =2
kubectl expose deployment web2 --port 9002 --target-port 8080
kubectl get deployments.apps
NAME   READY   UP-TO-DATE   AVAILABLE   AGE
web1 2 /2 2 2 2m3s
web2 2 /2 2 2 111s
kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT ( S )     AGE
kubernetes ClusterIP 10 .96.0.1 <none> 443 /TCP 39d
web1 ClusterIP 10 .99.37.121 <none> 9001 /TCP 89s
web2 ClusterIP 10 .102.94.47 <none> 9002 /TCP 81s
建立 Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-multiple
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: web1
port:
number: 9001
- path: /v2
pathType: Prefix
backend:
service:
name: web2
port:
number: 9002
defaultBackend:
service:
name: web1
port:
number: 9001
kubectl get ingress

Name Based Virtual Hosts with Ingress

virtual.png

建立 Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-multiple
spec:
ingressClassName: nginx
rules:
- host: v1.api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web1
port:
number: 9001
- host: v2.api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web2
port:
number: 9002

Using TLS certificates for HTTPs Ingress

生成 key
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=api.example.com"
Generating a RSA private key
...........+++++
................................+++++
writing new private key to 'tls.key'
-----
$ ls
tls.crt tls.key
生成 secret
$ kubectl create secret tls test-tls --key = "tls.key" --cert = "tls.crt"
secret/test-tls created
$ kubectl get secrets
NAME TYPE DATA AGE
test-tls kubernetes.io/tls 2 6s
加入 ssl
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-https
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: test-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web1
port:
number: 9001

Node IP & Pod IP & Cluster IP

  • Node IP 節點的物理網卡
  • Pod IP 根據 network bridge CIDR 分配的虛擬二層網路
  • Cluster IP 虛擬 IP,無法被 Ping,只能結合 Service POrt 組成一個具體的通信端口